package li.kaeppe.travel.tracker.dao;

import java.util.List;

import li.kaeppe.travel.tracker.domain.Trip;
import li.kaeppe.travel.tracker.domain.WayPoint;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class HibernateWayPointDaoImpl implements WayPointDao {
	
	/* **************************************************** */
	/*                      CONSTANTS                       */
	/* **************************************************** */
	private final static Log log = LogFactory.getLog(HibernateWayPointDaoImpl.class);

	/* **************************************************** */
	/*                     MEMBER FIELDS                    */
	/* **************************************************** */	
	private SessionFactory sessionFactory;
	
	private int batchSize = 10;
	
	private int pageSize = 200;

	/* **************************************************** */
	/*                    PUBLIC METHODS                    */
	/* **************************************************** */

	public WayPoint storeWayPoint(WayPoint point) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->storeWayPoint(): point [" + point.toString() + "]");
		}
		Session session = this.getSession();
		session.saveOrUpdate(point);
		return point;
	}
	
	public void storeWayPoints(List<WayPoint> points){
		Session session = this.getSession();
		
		for(int i = 0; i < points.size(); i++) {
			session.save(points.get(i));
			if( i % this.getBatchSize() == 0) {
				session.flush();
				session.clear();
			}
		}
		
	}
	
	
	public List<WayPoint> getAllWayPoints() {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getWayPoints()");
		}

		@SuppressWarnings("unchecked")
		List<WayPoint> points = this.getSession().createQuery("from WayPoint wayPoint").list();
		return points;
	}
	
	public List<WayPoint> getAllWayPoints(Trip trip) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getWayPoints(" + trip.toString() + ")");
		}
		
		@SuppressWarnings("unchecked")
		List<WayPoint> points = this.getSession().
		//createQuery("from WayPoint wayPoint where wayPoint.trip = :trip order by wayPoint.date desc").
				createQuery("from WayPoint wayPoint where wayPoint.trip = :trip").
				setEntity("trip", trip).
				list();
		return points;
	}
	
	@SuppressWarnings("unchecked")
	public List<WayPoint> getAllWayPoints(long tripId) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getWayPoints(" + tripId + ")");
		}
		
		List points = this.getSession().
				createQuery("from WayPoint wayPoint where wayPoint.trip.id = :tripId").
				setLong("tripId", tripId).
				list();
		return points;
	}
	
	public List<WayPoint> getPagedWayPoints(String tripKey, Integer pageNumber) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getPagedWayPoints(" + tripKey + ", " + pageNumber + ")");
		}
		
		@SuppressWarnings("unchecked")
		List<WayPoint> points = this.getSession().
			createQuery("from WayPoint wayPoint where wayPoint.trip.key = :tripKey order by wayPoint.date desc").
			setString("tripKey", tripKey).
			setFirstResult(Math.max(0, pageNumber * this.getPageSize() - 1)).
			setMaxResults(this.getPageSize()).
			list();
		return points;
	}
	
	public List<WayPoint> getPagedWayPoints(String tripKey, Integer pageNumber, int level) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getPagedWayPoints(" + tripKey + ", " + pageNumber + ")");
		}

		@SuppressWarnings("unchecked")
		List<WayPoint> points = this.getSession().
				//createQuery("from WayPoint wayPoint where wayPoint.trip.key = :tripKey order by wayPoint.date desc").
				createQuery("from WayPoint wayPoint where wayPoint.trip.key = :tripKey and wayPoint.level >= :level order by wayPoint.date desc").
				setString("tripKey", tripKey).
				setInteger("level", level).
				setFirstResult(Math.max(0, pageNumber * this.getPageSize() - 1)).
				setMaxResults(this.getPageSize()).
				list();
		return points;
	}
	
	public List<WayPoint> getPagedWayPoints(String tripKey, String username, Integer pageNumber, int level) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl->getPagedWayPoints(" + tripKey + ", " + pageNumber + ")");
		}

		@SuppressWarnings("unchecked")
		List<WayPoint> points = this.getSession().
				//createQuery("from WayPoint wayPoint where wayPoint.trip.key = :tripKey order by wayPoint.date desc").
				createQuery("from WayPoint wayPoint where wayPoint.trip.key = :tripKey and wayPoint.trip.user.username = :username and wayPoint.level >= :level order by wayPoint.date desc").
				setString("tripKey", tripKey).
				setString("username", username).
				setInteger("level", level).
				setFirstResult(Math.max(0, pageNumber * this.getPageSize() - 1)).
				setMaxResults(this.getPageSize()).
				list();
		return points;
	}
	
	public void deleteWayPoint(WayPoint point) {
		if(log.isDebugEnabled()) {
			log.debug("getWayPoints");
		}
		
		this.getSession().delete(point);
	}
	
	public WayPoint getWayPointById(long wayPointId) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateWayPointDaoImpl -> getWayPointById");
		}
		
		return (WayPoint)this.getSession().
		createQuery("from WayPoint wayPoint where wayPoint.id = :wayPointId").
		setLong("wayPointId", wayPointId).
		uniqueResult();
	}
	
	/* **************************************************** */
	/*                  PROTECTED METHODS                   */
	/* **************************************************** */

	/* **************************************************** */
	/*                   PRIVATE METHODS                    */
	/* **************************************************** */

	private Session getSession() {
		return this.getSessionFactory().getCurrentSession();
	}
	
	/* **************************************************** */
	/*                  GETTERS AND SETTERS                 */
	/* **************************************************** */

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public int getBatchSize() {
		return batchSize;
	}

	public void setBatchSize(int batchSize) {
		this.batchSize = batchSize;
	}
	
	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}
}
